﻿#include<iostream>
using namespace std;
//多态：完成某个行为，不同的对象有不同的形态
//不同继承关系的类对象，去调用同一函数，产生了不同的行为
//比如子类买票半价，父类买票全价
//构成条件：
//1，必须使用基类的（指针）或者引用调用（虚函数） 
// new   delete                 &赋值
//2，被调用的函数必须是虚函数，且派生类必须对基类的虚函数进行（重写）

//虚函数virtual（虚拟）不能修饰全局函数（只能修饰非静态的函数）
// 即被virtual修饰的类成员函数----->虚函数
//virtual void buy() {}

//虚函数下的重写/覆盖
//在派生类存在和父类一样的virtual虚函数（内部有不同）

//class A {};
//class B :public A {};
//
/*
class Person
{
public:
	//virtual A* BuyTicket() {
	virtual void BuyTicket() {
		cout << "买票--全价" << endl;
		//return 0;
	}

	virtual ~Person() { cout << "~person()" << endl; }
	//父类的virtual不能去掉
	//直接不能构成多态的要求
};

class Student : public Person
{
public:
	virtual void BuyTicket() { cout << "买票--半价" << endl; }
	//virtual B* BuyTicket() {cout << "买票--半价" << endl;return 0;}
	 //子类的virtual可以去掉
	 //继承了父类，也满足虚函数的重写
	 //但建议加上
	virtual ~Student() { cout << "~student()" << endl; }
};

//多态也发生在多个子类里
class Soldier : public Person {
public:
	virtual void BuyTicket() { cout << "买票--优先" << endl; }
	//virtual B* BuyTicket() {cout << "买票--优先" << endl;return 0;}
};


//void Func(Person p)
//上面的调用不构成多态
void Func(Person& p)//引用调用（必须有&）
{
	p.BuyTicket();//不能指定域，否则也不构成多态
}

void Func1(Person* p)//指针调用（必须有*）
{
	p->BuyTicket();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//void Func2(Person p)//类调用
//{
//	Person* ptr = &p;
//	//虽然函数体使用的是指针，但是传参都是Person类
//	//这个指针也是Person类型，调用的都是Person的买票
//	ptr->BuyTicket();
//}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

int main()
{
	Person p;
	Student st;
	Soldier so;

	//Func(p);
	//Func(s);
	

	Func1(&p);
	Func1(&st);
	Func1(&so);

	//类调用不会构成多态
	//Func2(p);
	//Func2(st);
	//Func2(so);
	//指向那个子类就调用哪个子类

	//Person* ptr_per = new Person();
	//ptr_per->BuyTicket();   // 指针调用

	//delete ptr_per;


	//Student stu;
	//Student& stuu = stu;
	//stuu.BuyTicket();   // 引用调用

	p.BuyTicket();
	s.BuyTicket();
	//多态
	return 0;
}
*/

//不构成多态则是隐藏（多态包含着：隐藏父类的同名函数）
//多态要求函数相同
//隐藏只要求函数名相同
//重写（更严格）属于隐藏
//重写是多态的条件之一
//隐藏包含着重写


//普通调用和传入对象的类型有关

//重写一般要求三同：（函数名，参数，返回类型）
//虚函数重写的两个例外：


// 协变（基类与派生类虚函数返回值类型不同）
// //这个类型不同：必须是父子类（其他两个父子）关系的指针/引用
//class A{};
//class B :public A{};
// //例如父类Person返回   A*
// //    子类Student返回  B*
// 更改了返回值类型，必须给一个返回值：例如return 0；

// 析构函数的重写
// 规定先析构父类再析构子类
// 在派生类析构结束后会自动调用父类
// 
//
class Person
{
public:
	//virtual ~Person() { cout << "~person()" << endl; }
	virtual ~Person() { cout << "~person()" << endl; }
};

class Student : public Person
{
public:
	//virtual ~Student() { cout << "~student()" << endl; }
	~Student() {
		//Person::~Person();

		delete _ptr;
		cout << "~student()" << endl;
	}
protected:
	//不加virtual
	//后果：如果有子类给父类的赋值操作（截断）
	// 那么析构时只调用了父类对象的析构，子类如果还有被保护的内存，就无法析构，释放空间
	//！！！！！导致内存泄漏
	int* _ptr = new int[10];
};
/*
int main()
{
	//Student s;
	//加不加virtual，普通对象都不受影响
	//这里先子后父，没问题
	//但是...
	Person* p1 = new Student;
//这里不加virtual就有影响（加上virtual就正常）
	//p1指向的是一个子类对象，但是调用的是父类的析构
	//没有调到派生类的析构函数：可能造成内存泄漏

	//实现多态就正常
	//析构函数会被隐藏
	// 子类调用完析构自动调用父类
	// 处理成destructor
	//p1->destructor() + operator delete(p1)
	delete p1;

	Person* p2 = new Person;
	delete p2;
	//多态析构就是：指向谁调用谁
	return 0;
}
*/
//面试题：析构函数是否建议设计为虚函数？
//  包的！！（因为符合指向谁调用谁）
//多态很重要


//不实现多态就最好不要写虚函数（会放入虚表）

//用override加在派生类函数的后面可以帮助判断是否成功完成函数重写（）
//如果不想再被重写，可以在父类函数后面加上final,后面的函数就不会重写这个函数
//final还可以实现一个：不能被继承的类
//class A final//最终类，不能被继承
//将构造函数私有，也可以构建一个不能被继承的类
//子类想要复用父类的构造函数就访问不了（派生类的对象就无法生成
// 
// 
/*
class A
{
public:
	// A CreateObj()
	//有这个函数的前提是
	static A CreateObj()
	{
		return A();
	}
	//间接调用:使用能调用的公有的函数
private:
	A()
	{}
};

class B : public A
{};

int main()
{
	//A aa;//实例化失败，因为构造不成功，在private里
	//但是如果想要调用类中的公有成员函数，就要先实例化，会出现矛盾，到底是先实例化才调用构造函数，还是先调用了构造函数才实例化成功
	//解决这个问题：给公有间接构造函数前加上static，静态化（）
	//创建对象的方式就变成了：
	//间接使用类域内公有的构造函数，去实例化对象（调用构造）
	A::CreateObj();
	B::CreateObj();
	//无法调用父类的构造也就无法生成派生类的对象
	return 0;
}
*/

//三个概念的对比：
// 
// 重载：两个函数在同一作用域
//      函数名相同，参数不同
// 
// 
// 重写（覆盖）：两个函数分别在基类和派生类的作用域
//              函数名/参数/返回值都必须相同(缺省值可以不同)（协变例外）
//              两个函数必须是虚函数
//
// 重定义（隐藏）：两个函数分别在基类和派生类的作用域
//               函数名相同
//               两个基类和派生类的同名函数不构成重写就是重载
// 
// 重写是特殊的隐藏


//笔试题（多态！！！！！！！！！！）
/*
class A
{
public:
	virtual void func(int val = 1) {std::cout << "A->" << val << std::endl; }
	virtual void test() { func(); }
	//两个func构成重写
	//多态的条件还有一个：用父类的指针/引用去访问
	//这里的this是A类型的指针
	//虽然是B的p指针，但是继承是可以用父类的func成员函数
	//但是这个成员函数本质上还是A*的this去使用func
	//父类指针/引用调用func构成多态
	//指针调用，指向的是子类对象（p = new B），调用的是子类的func
	

	//重点：
	//func构成重写
	//test内部，调用的是父类的指针（this）-> func();
	//构成了多态：
	//多态的条件下，指针指向的是B类型（派生类）
	//所以要使用派生类的同名函数

	//坑！！！！！！！！！！！！！！！！！！！！！！！！！！
	//函数的重写是：重写函数的实现，也就是函数体内部，
	//调用func时：是用func(int val = 1)和重写的函数体
	// { std::cout << "B->" << val << std::endl; }
	//拼接而成的函数，所以，如果缺省值不同的构成重写的函数，
	//符合多态时：进行多态的调用
	//则可能出现紊乱

	//答案：B->1

};

class B :public A
{
public:
	//private:
	//
	void func(int val = 0) { std::cout << "B->" << val << std::endl; }
};

int main()
{
	B* p = new B;
	p->test();//有了多态这个性质后
	//调用同名的函数 就要去看，是哪个对象的调用
	return 0;
}
*/
//不能实例化出对象）
// 抽象：不实际
// 纯虚函数  虚函数后面加上 = 0
/*
class Car
{
public:
	virtual void Drive() = 0;
};

class Benz :public Car
{
public:
};

class BWM :public Car
{
public:
	virtual void Drive()
	{
		cout << "BWM-操控" << endl;
	}

	//virtual void func() = 0;//派生类还有纯虚函数能否实例化出对象？
	//不能！！！！！！！！！！！！！！
	//定义：只要是包含了纯虚函数的类就叫做抽象类，无法实例化对象
};

int main()
{
	//Car car;//❌
	Benz car;//因为继承了纯虚函数，也是抽象类
	//如果想要实例化出继承了纯虚函数的子类
	//那么必须重写虚函数
	//BWM就是很好的重写的例子
	BWM car;
	return 0;
}
*/
//纯虚函数的使用场景：在现实中没有实际的对象（无法在实际中准确实例化出一个类，不想实例化：可以涉及为抽象类）

// 
//****************************************************************************************
//虚函数表（本质是一个（函数指针）数组）简称---虚表
//_vft
//
//

class Base
{
public:
	//两个虚函数，一个非虚函数
	//虚函数的地址才放在虚表，非虚函数
	virtual void Func1()
	{
		cout << "Base::Func1()" << endl;
	}
	virtual void Func2()
	{
		cout << "Base::Func2()" << endl;
	}
	void Func3()
	{
		cout << "Base::Func3()" << endl;
	}
private:
	int _b = 1;//8
	//char _ch = 'x';//1
};
class Derive:public Base
{
public:
	//两个虚函数，一个非虚函数
	//虚函数的地址才放在虚表，非虚函数
	virtual void Func1()
	{
		cout << "Derive::Func1()" << endl;
	}
private:
	int _d = 2;//8
	//char _ch = 'x';//1
};

void Func4(Base* p)
{
	//运行时绑定/动态绑定
	p->Func1();
	//p是Base对象，没有切割，在base的虚表去找func1
	//p如果是Derive对象：赋值转换发生切割
	//derive的父类的部分被切出来
	//这个虚表就变成了子类的虚表
	//调用就去子类的虚表，找重写的func1

	//编译时绑定/静态绑定
	p->Func3();//不是虚函数，没有放入虚表
	//普通函数和使用的对象没关系
	//调用的是函数内的Base有关
	
}
//func3不放入虚表，多态的调用会去查找虚表
// 
//
/*
int main()
{
	//Func4(new Base);
	//Func4(new Derive);
	//不同对象的虚表，存储的是不同的虚函数

	//base的func1/func3都没问题，实现了多态的调用
	//但是func3，无论是Derive指针调用/还是Base指针调用
	//都是使用函数本身（不多态）的参数p（Base*类型）

	//原因：多态的实现：就是通过虚表来实现的
	//base的虚表/derive的虚表，都存了func1
	//但是没有存入func3
	//导致func3不构成多态，无论谁调用，都是同样的（父类调用了func3）


	//derive，只重写了func1（derive的虚表要指向重写的func1）

	//引用
	Base b;
	Derive d;

	Func4(&b);
	Func4(&d);

	//指针
	//Base* b=new Base;
	//Derive* d=new Derive;

	//b->Func1();
	//b->Func3();
	//d->Func1();
	//d->Func3();

	//多态：指向谁调用谁&b/&d
	//谁调用，调用谁

	return 0;
}
*/

//int main()
//{
//	cout << sizeof(Base) << endl;
//	//8 + 4（指针）
//	//12个字节
//	Base b;
//	//因为定义了虚函数，需要把虚函数放入虚函数表
//	//除了int char，还有一个指针，指向表中的函数地址
//
//	//查看函数存储的地址
//	//cout << &Base::Func1 << endl;
//	//cout << &Base::Func2 << endl;
//	//cout << &Base::Func3 << endl;
//	//1 1 1
//	//与c++流提取的重载有关系
//	//打印错误
//	//1，强转其他类型再打印
//	//2，用printf
//	printf("%p\n", &Base::Func1);
//	printf("%p\n", &Base::Func2);
//	printf("%p\n", &Base::Func3);
//	//00841D7A//00841D6B//00841D70
//
//	return 0;
//}


//实现动物类animal

class Animal
{
public:
	virtual void sound() const= 0;//写为纯虚函数，因为动物是一个抽象的类别，无法精确描述”动物“这个大体的叫声

			
};

class Cat :public Animal
{
public:
	virtual void sound() const
	{
		cout << "喵喵~~" << endl;
	}
};

class Dog :public Animal
{
	virtual void sound() const
	{
		cout << "汪汪~~" << endl;
	}
};

void AnimalSound(const Animal& anm)//父类指针/引用
{
	anm.sound();//调用传入的对象（引用）的sound函数
}

//int main()
//{
//	AnimalSound(Cat());//调用时传入的是cat对象的引用
//	//就去调用cat重写的sound函数
//	//喵喵~~
//	AnimalSound(Dog());
//
//	return 0;
//}



//虚函数在哪（代码段）
//虚函数表在哪（代码段）
// 虚函表数指针在哪、（对象里）
// 
//

//菱形继承类地址题
//class Base1 { public:int _b1; };
//class Base2 { public:int _b2; };
//class Derive :public Base1, public Base2 { public:int _d; };

//int main()
//{
//	Derive d;
//	Base1* p1 = &d;
//	Base2* p2 = &d;
//	Derive* p3 = &d;
//	return 0;
//}
//答案：p1=p3!=p2
//         
// 内存中：是按声明的顺序存储
//        p1->         Base1        }   <-p3
//                                  } 
//        p2->         Base2        }
//                                  }
//                     {     }      }














































































